"""
D5 插件功能演示模块
功能：实现与D5渲染器的插件交互，包括模型列表获取和界面交互
"""

import sys

# import debugpy  # 调试模块应在正式发布时移除
# debugpy.connect(('localhost', 5678))  # 连接调试器


# LOG_FILE = open('D:/output.log', 'w', encoding='utf-8')  
# sys.stdout = LOG_FILE
# sys.stderr = LOG_FILE
import json
import time
from pathlib import Path
from typing import List, Optional, Dict, Any

# 第三方库导入
from PySide2.QtWidgets import (
    QApplication, QWidget, QLabel, QLineEdit, QPushButton, 
    QVBoxLayout, QHBoxLayout, QFormLayout, QFileDialog, 
    QScrollArea, QCheckBox
)
from PySide2.QtCore import Qt, QStringListModel

# 本地模块导入
from D5Core.D5Tools import ModelList, SceneTool, RenderTool
from D5Core.D5Widget import D5Plugin
from D5Core.D5Models import D5Model
from D5Core.D5Common import *
from D5Core.D5Lights import PointLight
import d5a

class D5ATool(D5Plugin):
    """D5A头图生成器插件"""
    
    # 配置常量
    DEFAULT_SCALE_TARGET = 100  # 默认缩放目标尺寸
    DEFAULT_RENDER_WIDTH = 1024  # 默认渲染宽度
    DEFAULT_RENDER_HEIGHT = 1024  # 默认渲染高度
    SCENE_SWITCH_DELAY = 3  # 场景切换等待时间(秒)
    RENDER_DELAY = 2  # 渲染等待时间(秒)
    
    def __init__(self, plugin_id: str):
        """初始化插件
        
        Args:
            plugin_id: 插件唯一标识符
        """
        super().__init__(plugin_id)
        self.ui = d5a.Ui_D5PluginDemo()
        self.ui.setupUi(self)
        self.d5aList: List[str] = []
        self.outputDir: Optional[str] = None
        
        # 连接信号和槽
        self._connect_signals()
    
    def _connect_signals(self) -> None:
        """连接UI信号和槽"""
        self.ui.startButton.clicked.connect(self.start_func)
        self.ui.inputDirSelectButton.clicked.connect(self.select_input_dir_func)
        self.ui.outputDirSelectButton.clicked.connect(self.select_output_dir_func)

    def select_output_dir_func(self) -> None:
        """选择输出目录"""
        dir_path = self.select_directory()
        if dir_path:
            self.ui.outputDirEdit.setText(dir_path)
            self.outputDir = dir_path
            self.log(f"输出目录已设置: {dir_path}")
        else:
            self.outputDir = None

    def select_input_dir_func(self) -> None:
        """选择输入目录并扫描d5a文件"""
        dir_path = self.select_directory()
        if not dir_path:
            return
            
        self.ui.inputDirEdit.setText(dir_path)
        self.d5aList.clear()
        
        try:
            d5a_files = self.get_all_files(dir_path, ".d5a")
            self.d5aList.extend(d5a_files)
            self.log(f"输入目录已设置: {dir_path}")
            self.log(f"找到 {len(d5a_files)} 个d5a文件")
        except Exception as e:
            self.log(f"扫描目录时出错: {e}")

    def get_all_files(self, dir_path: str, extension: Optional[str] = None) -> List[str]:
        """获取目录下所有指定扩展名的文件
        
        Args:
            dir_path: 目录路径
            extension: 文件扩展名（包含点号，如'.d5a'）
            
        Returns:
            文件路径列表
        """
        if not dir_path or not Path(dir_path).is_dir():
            return []
            
        files = []
        try:
            for file in Path(dir_path).rglob('*'):
                if file.is_file() and (extension is None or file.suffix == extension):
                    files.append(str(file.resolve()))
        except PermissionError as e:
            self.log(f"权限错误，无法访问某些文件: {e}")
        except Exception as e:
            self.log(f"扫描文件时出错: {e}")
            
        return files

    def select_directory(self) -> Optional[str]:
        """打开目录选择对话框
        
        Returns:
            选择的目录路径，如果取消则返回None
        """
        dir_path = QFileDialog.getExistingDirectory(
            self, 
            "选择目录", 
            "", 
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
        )
        return dir_path if dir_path else None

    def start_func(self) -> None:
        """开始处理所有d5a文件"""
        # 输入验证
        if not self.d5aList:
            self.log("❌ 没有可处理的文件，请先选择输入目录")
            return
            
        if not self.outputDir:
            self.log("❌ 请先选择输出目录")
            return
            
        if not Path(self.outputDir).exists():
            self.log("❌ 输出目录不存在")
            return
            
        self.log(f"🚀 开始处理 {len(self.d5aList)} 个文件...")
        
        success_count = 0
        for i, d5a_file in enumerate(self.d5aList, 1):
            if not d5a_file:
                self.log("⚠️ 跳过空文件路径")
                continue
                
            self.log(f"📁 [{i}/{len(self.d5aList)}] 正在处理: {Path(d5a_file).name}")
            
            try:
                self.handle_d5a(d5a_file)
                success_count += 1
                self.log(f"✅ [{i}/{len(self.d5aList)}] 处理完成")
            except FileNotFoundError as e:
                self.log(f"❌ [{i}/{len(self.d5aList)}] 文件未找到: {e}")
            except PermissionError as e:
                self.log(f"❌ [{i}/{len(self.d5aList)}] 权限错误: {e}")
            except json.JSONDecodeError as e:
                self.log(f"❌ [{i}/{len(self.d5aList)}] JSON解析错误: {e}")
            except Exception as e:
                self.log(f"❌ [{i}/{len(self.d5aList)}] 处理失败: {e}")
                
        self.log(f"🎉 处理完成！成功: {success_count}/{len(self.d5aList)}")

    def handle_d5a(self, d5a_file: str) -> None:
        """处理单个d5a文件
        
        Args:
            d5a_file: d5a文件路径
            
        Raises:
            FileNotFoundError: 文件不存在
            ValueError: 无效的文件格式
            Exception: 其他处理错误
        """
        # 输入验证
        d5a_path = Path(d5a_file)
        if not d5a_path.exists():
            raise FileNotFoundError(f"文件不存在: {d5a_file}")
        
        if not self.outputDir or not Path(self.outputDir).exists():
            raise ValueError(f"输出目录无效: {self.outputDir}")
        
        # 获取文件信息
        d5a_folder = d5a_path.parent
        name = d5a_folder.stem
        
        model = None  # 用于资源清理
        
        try:
            # 查找并切换场景
            scene_name = self._find_and_switch_scene(name)
            
            # 导入和创建模型
            model = self._import_and_create_model(d5a_file)
            
            # 设置模型属性
            self._setup_model_properties(model)

            time.sleep(self.SCENE_SWITCH_DELAY)  # 等待场景切换完成
            
            # 渲染图片
            output_path = Path(self.outputDir) / f"{d5a_path.stem}.png"
            self._render_image(model, output_path, scene_name)
            
        finally:
            # 清理资源
            if model:
                try:
                    ModelList().delete_model(model._unique_actor_id)
                    self.log("🧹 模型资源已清理")
                except Exception as e:
                    self.log(f"⚠️ 清理模型资源失败: {e}")
                    
            # 等待渲染完成
            self._wait_for_completion(self.RENDER_DELAY)
    
    def log(self, message):
        """向日志区域添加消息"""
        self.ui.logArea.append(message)
        # 强制刷新UI界面，确保日志能及时显示
        QApplication.processEvents()
        # 滚动到底部显示最新日志
        self.ui.logArea.ensureCursorVisible()

    def _find_and_switch_scene(self, name: str) -> str:
        """查找并切换到匹配的场景
        
        Args:
            name: 场景名称关键字
            
        Returns:
            切换到的场景名称
        """
        scenes = SceneTool().get_scenes()
        scene_name = ""
        
        for scene_id, scene in scenes.items():
            if name in scene.name:
                scene_name = scene.name
                self.log(f"🎬 找到匹配场景: {scene.name} (ID: {scene_id})")
                SceneTool().switch_scene(scene_id)
                self.log("⏳ 等待场景切换完成...")
                self._wait_for_completion(self.SCENE_SWITCH_DELAY)
                break
        
        if not scene_name:
            self.log(f"⚠️ 未找到匹配场景: {name}")
            
        return scene_name
    
    def _import_and_create_model(self, d5a_file: str):
        """导入并创建模型
        
        Args:
            d5a_file: d5a文件路径
            
        Returns:
            创建的模型对象
            
        Raises:
            ValueError: 模型创建失败
            json.JSONDecodeError: JSON解析失败
        """
        self.log("📦 开始导入模型资源...")
        res = ModelList().import_resource_data(d5a_file)
        self.log(f"📦 导入结果: {res}")
        
        uuid = ""
        if res:
            try:
                parsed_res = json.loads(res)
                uuid = parsed_res.get("uuid", "")
            except json.JSONDecodeError as e:
                raise json.JSONDecodeError(f"解析导入结果失败: {e}")
        
        if not uuid:
            raise ValueError("未能获取模型UUID")
            
        self.log("🔨 开始创建模型...")
        model = ModelList().create_model(uuid)
        
        if not model:
            raise ValueError(f"模型创建失败，UUID: {uuid}")
            
        self.log("✅ 模型创建成功")
        return model
    
    def _setup_model_properties(self, model:D5Model) -> None:
        """设置模型属性
        
        Args:
            model: 模型对象
        """
        self.log("⚙️ 开始设置模型属性...")
        
        # 获取原始尺寸并计算缩放
        size = model.get_original_size()
        max_dimension = max(size.x, size.y, size.z)
        
        if max_dimension <= 0:
            self.log("⚠️ 模型尺寸异常，使用默认缩放")
            scale_factor = 1.0
        else:
            scale_factor = self.DEFAULT_SCALE_TARGET / max_dimension
            
        model.set_scale(Vector3(scale_factor, scale_factor, scale_factor))
        self.log(f"📏 设置缩放比例: {scale_factor:.4f}")

        # 根据配置文件设置
        # model.set_rotation(Vector3(0,0,0))

        # 设置模型位置
        if model.get_object_type() == D5ApiActorType.FusionGroup:
            current_size = model.get_size()
            offset = current_size.z / 2
            model.set_location(Vector3(0, 0, offset))
            self.log(f"📍 设置模型位置: (0, 0, {offset:.2f})")
    
    def _render_image(self, model, output_path: Path, scene_name: str) -> None:
        """渲染图片
        
        Args:
            model: 模型对象
            output_path: 输出文件路径
            scene_name: 场景名称
        """
        self.log("🎨 开始渲染...")
        
        RenderTool().render_photo(
            path=str(output_path),
            width=self.DEFAULT_RENDER_WIDTH,
            height=self.DEFAULT_RENDER_HEIGHT,
            channel_graph_choose=[3, 5],
            channel_graph=1,
            format=0,
            scene_name=scene_name
        )
        
        self.log(f"🖼️ 渲染完成: {output_path}")
    
    def _wait_for_completion(self, delay: float) -> None:
        """等待操作完成
        
        Args:
            delay: 等待时间（秒）
        """
        time.sleep(delay)
        QApplication.processEvents()  # 处理UI事件

    
def load_config(file_path):
    """加载插件配置"""
    config_path = Path(file_path)
    if not config_path.exists():
        raise FileNotFoundError(f"配置文件 {file_path} 不存在")
    
    try:
        with config_path.open('r', encoding='utf-8') as f:
            return json.load(f)
    except json.JSONDecodeError as e:
        print(f"JSON 解析错误: {e}")
        return {}  # 返回空字典可能导致后续问题，需要调用方处理

# 加载配置
plugin_dir = Path(__file__).parent
config = load_config(plugin_dir / "package.json")
plugin_id = config.get("id", "default")

# 创建插件实例
D5ATool(plugin_id)